package org.hismig.cfg.partition;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.hismig.cfg.DBConnection;
import org.hismig.cfg.Param;
import org.hismig.utils.CheckTool;
import org.hismig.utils.FileTool;
import org.hismig.utils.StringTool;

/**
 * @declare: //TODO <br />
 *           This class created by Vansion.Hopihit on 2012-07-04 <br />
 *           <a href="mailto:vansionhopihit@gmail.com">Contact Me</a>
 * @version 1.0
 * @author Vansion.Hopihit
 * 
 */
public class Configuration {

	private static final String E_ROOT = "HisMigShard";
	private static final String E_DBCONNECTION = "DBConnection";
	private static final String E_PARAM = "Param";

	private static final String A_ENCRYPT = "encrypt";
	private static final String A_NAME = "name";

	private static final String F_CFGFILE = "HisMig-shard-%s.cfg.xml";
	private static final String F_CFGPATH = "Shard";

	public static List<DBConnection> getShardDBConnections(String dbName) {

		List<DBConnection> dbConnections = null;

		if (checkCfgFile(dbName)) {
			String fileName = String.format(F_CFGFILE, dbName);

			try {
				Document doc = new SAXReader().read(FileTool.getRuntimeFile(
						fileName, F_CFGPATH));
				dbConnections = setupDbConnections(doc);
			} catch (DocumentException e) {
				throw new IllegalArgumentException(String.format(
						"Can't read the config file : %s", fileName), e);
			}

		} else {
			dbConnections = new ArrayList<DBConnection>();
		}

		return dbConnections;
	}

	public static boolean checkCfgFile(String dbName) {
		String fileName = String.format(F_CFGFILE, dbName);

		return FileTool.getRuntimeFile(fileName, F_CFGPATH).exists();
	}

	@SuppressWarnings("unchecked")
	private static List<DBConnection> setupDbConnections(Document doc) {
		List<DBConnection> dbConnections = new ArrayList<DBConnection>();

		List<Node> nodes_dbConnection = doc.selectNodes(String.format("/%s/%s",
				new Object[]{E_ROOT, E_DBCONNECTION}));

		// must to have database cfg
		CheckTool.checkNotNull(nodes_dbConnection);
		CheckTool.checkArgument(!nodes_dbConnection.isEmpty());

		for (Node node_dbConnection : nodes_dbConnection) {

			DBConnection dbConnection = new DBConnection();

			String str_encrypt = StringTool.trimBlank(node_dbConnection
					.valueOf("@" + A_ENCRYPT));
			// CheckTool.checkNotEmpty(str_encrypt);

			dbConnection.setEncrypt(StringTool.convertToBoolean(str_encrypt,
					true));
			dbConnection.setParams(setupParams(node_dbConnection));
			
			dbConnections.add(dbConnection);
		}

		return dbConnections;
	}

	@SuppressWarnings("unchecked")
	private static List<Param> setupParams(Node node) {
		List<Param> params = new ArrayList<Param>();
		List<Node> nodes_param = node.selectNodes("./" + E_PARAM);
		if (null != nodes_param) {
			for (Node node_param : nodes_param) {
				Param param = new Param();
				param.setName(StringTool.trimBlank(node_param.valueOf("@"
						+ A_NAME)));
				param.setValue(StringTool.trimBlank(node_param.getText()));
				params.add(param);
			}
		}

		return params;
	}

	public static void saveOrUpdateXmlFile(String dbName,
			List<DBConnection> dbConnections) {
		Document doc = DocumentHelper.createDocument();

		Element root = doc.addElement(E_ROOT);
		for (DBConnection dbConnection : dbConnections) {
			Element e_dbConnection = root.addElement(E_DBCONNECTION);

			e_dbConnection.addAttribute(A_ENCRYPT,
					String.valueOf(dbConnection.isEncrypt()));

			List<Param> params = dbConnection.getParams();

			for (Param param : params) {
				Element e_param = e_dbConnection.addElement(E_PARAM);

				e_param.addAttribute(A_NAME, param.getName());
				e_param.setText(param.getValue());
			}

		}

		XMLWriter writer = null;
		String fileName = String.format(F_CFGFILE, dbName);
		try {
			OutputFormat output = OutputFormat.createPrettyPrint();
			output.setEncoding("UTF-8");
			output.setLineSeparator(System.getProperty("line.separator"));

			writer = new XMLWriter(new FileWriter(FileTool.getRuntimeFile(
					fileName, F_CFGPATH)), output);

			writer.write(doc);
		} catch (IOException e) {
			throw new RuntimeException(String.format(
					"Can't save the XML file : %s", fileName), e);
		} finally {
			try {
				if (null != writer) {
					writer.close();
					writer = null;
				}
			} catch (IOException e) {
				throw new RuntimeException(String.format(
						"Can't close writer when save the XML file : %s",
						fileName), e);
			}
		}
	}

	public static DBConnection makeDBConnection(Map<String, String> paramsMap,
			boolean encrypt) {
		CheckTool.checkNotNull(paramsMap);

		DBConnection dbConnection = new DBConnection();
		dbConnection.setEncrypt(encrypt);
		dbConnection.setParent(null);

		List<Param> params = new ArrayList<Param>();

		for (Entry<String, String> paramEntry : paramsMap.entrySet()) {
			Param param = new Param();
			param.setName(paramEntry.getKey());
			param.setValue(paramEntry.getValue());
			param.setParent(dbConnection);

			params.add(param);
		}

		dbConnection.setParams(params);

		return dbConnection;
	}
}
